package translation;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class OneToMany implements RuleTypeConstants{
	private String fromXPath;
	private String fromRegEx;
	private List<VarBinding> vb = null;
	private Map<String, String> toFields; //<toXPath, toRegex>

	public OneToMany(String fromXPath, String fromRegEx) {
		this.fromXPath = fromXPath;
		this.fromRegEx = fromRegEx;
		toFields = new LinkedHashMap<String, String>();
	}

	public String getFromXPath() {
		return fromXPath;
	}

	public void setFromXPath(String fromXPath) {
		this.fromXPath = fromXPath;
	}

	public String getFromRegEx() {
		return fromRegEx;
	}

	public void setFromRegEx(String fromRegEx) {
		this.fromRegEx = fromRegEx;
	}

	public Map<String, String> getToFields() {
		return toFields;
	}	
	public void addToField(String toXPath, String toRegex){
		if(toFields == null)
			toFields = new HashMap<String, String>();
		
		toFields.put(toXPath, toRegex);
	}	

	public List<VarBinding> getVariableBinding(){
		return vb;
	}
	public List<VarBinding> initVariableBinding(){
		vb = new ArrayList<VarBinding>();
		return vb;
	}
	public List<VarBinding> addVariableBinding(VarBinding v){
		if(vb == null)
			vb = new ArrayList<VarBinding>();
		vb.add(v);
		return vb;
	}
	public void removeVarBinding(int index){
		if(vb != null){
			vb.remove(index);
		}
	}
	
	public void removeVarBinding(VarBinding v){
		if(vb != null){
			vb.remove(v);
		}
	}

	/**
	 * Writes the OneToMany structure to a file
	 * @param output The file to 
	 */
	public void writeToXMLFile(File output){

		BufferedWriter bw = null;
		try {
			//Initialize output file
			if (!output.exists()) {
				output.createNewFile();
			}
			FileWriter fw = new FileWriter(output.getAbsoluteFile(),true); //append to existing file
			bw = new BufferedWriter(fw);

			//write basic tag
			bw.write(OneToManyStartingTag);

			/* From Field:
			 * In the case of OneToMany, fromRegex always exists as 
			 * there must be a way to separate the token to multiple to fields.
			 * Thus From Field Type is always FilteredSourceFieldType.
			 * Variable Binding is possible, except from Explicit Binding
			 */
			
			bw.write(FilteredSourceFieldTypeStart);
			bw.write(XPathExpressionStart + fromXPath + XPathExpressionTerminating);

			if(fromRegEx != null){						
				bw.write(FilteredWithRegexStart + fromRegEx + FilteredWithRegexTerminating);
			}	

			if(vb != null){
				for(VarBinding v : vb){
					bw.write(VariableBindingStart);
					if(v.getExplicitPrefixBinding() !=null){
						for(String s : v.getExplicitPrefixBinding()){
							bw.write(ExplicitPrefixBindingStart + s + ExplicitPrefixBindingTerminating);
						}
					}
					else if(v.getListBinding() != null){
						bw.write(ListBindingStart + v.getListBinding() + ListBindingTerminating);
					}
					else if(v.getExplicitPostfixBinding() != null){
						for(String s : v.getExplicitPostfixBinding()){
							bw.write(ExplicitPostfixBindingStart + s + ExplicitPostfixBindingTerminating);
						}
					}
					
					bw.write(VariableBindingTerminating);
				}
			}

			bw.write(TerminatingFromField);

			/*
			 * To Fields:
			 * There are occasions that only XPath exists, or along with regex
			 * e.g. TargetFieldType
			 * <XPathExpression>/JPSearchCore/Modifiers/GivenName/$a</XPathExpression>
			 * 
			 * OR FormattedTargetFieldType
			 * XPathExpression>/JPSearchCore/Modifiers/GivenName</XPathExpression>
			 * <ReplaceWithRegExpr>Mr. $1</ReplaceWithRegExpr>
			 * 
			 * Code below:
			 * Iterates through the toFields and writes the XML according to the fields
			 * 
			 */
			
			Iterator<Map.Entry<String, String>> it = toFields.entrySet().iterator();
			while (it.hasNext()) {	
				Map.Entry<String, String> pair = it.next();
				String toXPath = pair.getKey();
				String toRegex = pair.getValue();
				
				//To Regex not exists => TargetFieldType
				if(toRegex == null){
					bw.write(TargetFieldTypeStart);
					bw.write(XPathExpressionStart + toXPath + XPathExpressionTerminating);
				}
				else{
					bw.write(FormattedTargetFieldTypeStart);
					bw.write(XPathExpressionStart + toXPath + XPathExpressionTerminating);
					bw.write(ReplaceWithRegExprStart + toRegex + ReplaceWithRegExprTerminating);
				}
				bw.write(TargetFieldTypeClosing);
			}

			//Write closing tag
			bw.write(TerminatingTag);

		} catch (IOException e) {
			e.printStackTrace();
		}
		finally{
			try {
				bw.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * Converts the object to a OneToManyFieldTranslationType and returns it
	 * @return The converted element
	 */
	public OneToManyFieldTranslationType toOneToManyFieldTranslationType(){
		ObjectFactory of = new ObjectFactory();		
		
		//Create object
		OneToManyFieldTranslationType o2mft = of.createOneToManyFieldTranslationType();
		
		//From Field
		if(fromRegEx == null && vb == null){
			SourceFieldType st = of.createSourceFieldType();
			st.setXPathExpression(fromXPath);
			o2mft.setFromField(st);
		}
		else{
			FilteredSourceFieldType fst = of.createFilteredSourceFieldType();
			fst.setXPathExpression(fromXPath);
			fst.setFilterWithRegExpr(fromRegEx);
			if(vb != null)
				for(VarBinding v : vb){
					fst.getVariableBinding().add(v.toVariableBinding());
				}	
			o2mft.setFromField(fst);		
		}
		
		//To Fields		
		Iterator<Map.Entry<String, String>> it = toFields.entrySet().iterator();
		while (it.hasNext()) {	
			Map.Entry<String, String> pair = it.next();
			String toXPath = pair.getKey();
			String toRegex = pair.getValue();
			
			//To Regex not exists => TargetFieldType
			if(toRegex == null){
				TargetFieldType tft = of.createTargetFieldType();
				tft.setXPathExpression(toXPath);
				o2mft.getToField().add(tft);
			}
			else{
				FormattedTargetFieldType ftft = of.createFormattedTargetFieldType();
				ftft.setXPathExpression(toXPath);
				ftft.setReplaceWithRegExpr(toRegex);
				o2mft.getToField().add(ftft);
			}
		}
		
		return o2mft;
	}
}